package jumavidesktop.Persistencia.Dao;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.Query;
import jumavidesktop.LibVimso.AuxVimso.Movimiento;
import jumavidesktop.Otros.Pojos.MyRespuesta;
import jumavidesktop.Otros.Pojos.consNamed;
import jumavidesktop.Persistencia.Dao.Interfaces.IDao;
import jumavidesktop.configuracion.Configuracion;

/**
 *
 * @author vimso
 */
public abstract class AbstractDAO<T> implements IDao<T>{

    //<editor-fold defaultstate="collapsed" desc="Enumeraciones">
    public enum TipoAccionBBDD {
        Creacion, Actualizacion, Lectura, Eliminacion
    };
    //</editor-fold>
    //<editor-fold defaultstate="collapsed" desc="Atributos">
    protected EntityManager entityManager;
    //</editor-fold>
    //<editor-fold defaultstate="collapsed" desc="Contructores">

    public AbstractDAO() {
        //asigno el contexto de persistencia por defecto
        this( Configuracion.getInstance().getEntityManager());
        //
    }

    public AbstractDAO( EntityManager entityManager) {
        this.entityManager = entityManager;
    }
    //</editor-fold>
    //<editor-fold defaultstate="collapsed" desc="Operaciones">
    //<editor-fold defaultstate="collapsed" desc="Creación">

    protected MyRespuesta agregarElemento(T elemento) {
        return this.operarElemento(elemento, TipoAccionBBDD.Creacion);
    }

    protected MyRespuesta actualizarElemento(T elemento) {
        return this.operarElemento(elemento, TipoAccionBBDD.Actualizacion);
    }

    protected MyRespuesta eliminarElemento(T elemento) {
        return this.operarElemento(elemento, TipoAccionBBDD.Eliminacion);
    }

    private MyRespuesta operarElemento(T elemento, TipoAccionBBDD tipoAccion) {
        List<T> elementos = new ArrayList<T>();
        elementos.add(elemento);
        return this.operarElementos(elementos, tipoAccion);
    }

    protected  MyRespuesta agregarElementos(List<T> elementos) {
        return this.operarElementos(elementos, TipoAccionBBDD.Creacion);
    }
    //</editor-fold>
    //<editor-fold defaultstate="collapsed" desc="Actualización">

    protected  MyRespuesta actualizarElementos(List<T> elementos) {
        return this.operarElementos(elementos, TipoAccionBBDD.Actualizacion);
    }

    private MyRespuesta operarElementos(List<T> elementos, TipoAccionBBDD tipoAccion) {
        
        MyRespuesta respu = this.verificarEntidades(elementos, tipoAccion);
        WrapperTransaction wTr= new WrapperTransaction(this.entityManager.getTransaction());
        
        if (respu.getTodoOK()) {            
            try {
                wTr.begin();
                for (T t : elementos) {
                    switch (tipoAccion) {
                        case Creacion:
                            this.entityManager.persist(t);
                            break;
                        case Actualizacion:
                            this.entityManager.merge(t);
                            break;
                        case Eliminacion:
                            this.entityManager.remove(t);
                            break;
                    }
                }
                wTr.commit();
            } catch (Exception e) {
                wTr.rollback();
                System.out.println(e);
                String mensaje="";
                switch (tipoAccion)
                {
                    case Actualizacion:
                        mensaje="No se ha podido actualizar los cambios en la base de datos";
                        break;
                    case Creacion:
                        mensaje="No se ha podido guardar los cambios en la base de datos";
                        break;
                    case Eliminacion:
                        mensaje="No se ha podido eliminar los datos en la base de datos";
                        break;
                }
                respu.addError(mensaje);
                
            } finally {
                return respu;
            }
        } else {
            return respu;
        }
    }
protected T encuentraRegistro(Class<T> clase, Object primaryKey)
{
    return this.entityManager.find(clase, primaryKey);
}    
    
    protected int count() {
        javax.persistence.criteria.CriteriaQuery cq = this.entityManager.getCriteriaBuilder().createQuery();
        javax.persistence.criteria.Root<T> rt = cq.from(this.getClase());
        cq.select(this.entityManager.getCriteriaBuilder().count(rt));
        javax.persistence.Query q = this.entityManager.createQuery(cq);
        return ((Long) q.getSingleResult()).intValue();
    }
    
    protected boolean existenRegistros()
    {
        return (this.count()>0)?true:false;
    }
    //</editor-fold>
    //<editor-fold defaultstate="collapsed" desc="Eliminación">

    protected  MyRespuesta eliminarElementos(List<T> elementos) {
        return this.operarElementos(elementos, TipoAccionBBDD.Eliminacion);
    }
    //</editor-fold>
    //<editor-fold defaultstate="collapsed" desc="Verificaciones">

    public MyRespuesta verificarEntidades(List<T> entidades, TipoAccionBBDD accion) {
        MyRespuesta respuesta = new MyRespuesta();
        MyRespuesta respuestaAux = new MyRespuesta();

        for (T t : entidades) {
            if(t==null)
            {
                return new MyRespuesta("Hay alguna entidades a null de tipo: "+t.getClass());
                
            }else{
            switch (accion) {
                case Creacion:
                    respuestaAux = this.verificarEntidadAlGuardar(t);
                    break;
                case Actualizacion:
                    respuestaAux = this.verificarEntidadAlModificar(t);
                    break;
                case Eliminacion:
                    respuestaAux = this.verificarEntidadAlEliminar(t);
                    break;
            }
            }
            if (!respuestaAux.getTodoOK()) {
                respuesta.setTodoOK(Boolean.FALSE);
                respuesta.addErrores(respuestaAux.getMensajes());
            }
        }
        return respuesta;
    }
    
    
    //</editor-fold>
    //<editor-fold defaultstate="collapsed" desc="Consultas y Movimientos">    
    
    protected List<T>  consultaNMBBDD(consNamed infoNamedQuery)
    {
        Query consulta = this.entityManager.createNamedQuery(infoNamedQuery.getNamedQuery());
        for ( Map.Entry<String,Object> elemento : infoNamedQuery.getParametros().entrySet()) {
            consulta.setParameter(elemento.getKey(),elemento.getValue());
        }
        return consulta.getResultList();        
    }    
    
    protected T consultaNMBBDDPrimero(consNamed armaNamedQuery)
    {
        List<T> elementos=this.consultaNMBBDD(armaNamedQuery);
        return elementos.get(0);
    }
    
    protected List<T>  consultaJPBBDD(String consultaJPQL)
    {
        Query consulta = this.entityManager.createQuery(consultaJPQL);
        return consulta.getResultList();        
    }
    
    protected void ejecutaJPQL(String JPQL) {
        EntityTransaction tx = this.entityManager.getTransaction();
        tx.begin();
        Query q = this.entityManager.createQuery(JPQL);
        q.executeUpdate();
        tx.commit();
    }

    @Override
    public T getEntidadMovimiento(String entidad,String campoOrden,String valorParametro,Movimiento movi,String campoid, String idEntidad)
    {
        String cadenaJPQL = null;
        List<T> entidades = null;

        //se puede tener entidad nula (id nulo), pero hay que comprobar si queremos agregar el primer registro, en cuyo caso se ignora.
        if(movi.equals(movi.INICIO) || !idEntidad.isEmpty()){
            cadenaJPQL=this.generaJPQLMovimiento(entidad, campoOrden, valorParametro, movi,campoid,idEntidad);        
            entidades=this.consultaJPBBDD(cadenaJPQL); 
        }

        if (entidades != null) {
            if (!entidades.isEmpty()) {
                return entidades.get(0);
            }
        }
        return null;
    }
    
    private String generaJPQLMovimiento(String entidad,String campoOrden,String valorParametro,Movimiento movi, String campoid, String id)    
    {        
        String where="";
        String orderBy="ORDER BY e.";    
        String orderByTipo = ""; //Descendente o ascendente: DESC, ASC (por defecto)
        
        //Según el tipo de movimiento cargo las varaibles
        switch(movi)
        {
            case INICIO:
                //Selecciono el primero  en orden ascendente
                //orden
                orderByTipo="ASC";
                orderBy= orderBy+ campoOrden+" "+orderByTipo;
                
                break;
            case ANTERIOR:                
                //orden
                orderByTipo = "DESC";
                orderBy = orderBy + campoOrden + " " + orderByTipo + ", e." + campoid + " " + orderByTipo;

                   String consinterna = "SELECT u." + campoid + " FROM " + entidad + " u WHERE u." + campoOrden + " = '" + valorParametro + "' AND u." + campoid + " >= " + id;
                    where = " WHERE e." + campoOrden + " <= '" + valorParametro + "' AND e." + campoid + " NOT IN (" + consinterna + ")";
                    
                break;
            case SIGUIENTE:
//                orden
                orderByTipo = "ASC";
                orderBy = orderBy + campoOrden + " " + orderByTipo;
                
                String consinterna2 = "SELECT u." + campoid + " FROM " + entidad + " u WHERE u." + campoOrden + " = '" + valorParametro + "' AND u." + campoid + " <= " + id;
                    where = " WHERE e." + campoOrden + " >= '" + valorParametro + "' AND e." + campoid + " NOT IN (" + consinterna2 + ")";

                break;
            case ULTIMO:
                orderByTipo="DESC";
                orderBy= orderBy+ campoOrden+" "+orderByTipo;
                break;
            default:
        }
        return "SELECT e FROM "+ entidad+" e "+ where +" "+orderBy;
    }
    
    //</editor-fold>
    //</editor-fold>
    //<editor-fold defaultstate="collapsed" desc="Métodos a implementar">

    
    public abstract MyRespuesta verificarDatosMinimos(T entidad);
    
    public abstract MyRespuesta verificarEntidadAlGuardar(T entidad);    

    public abstract MyRespuesta verificarEntidadAlModificar(T entidad);

    public abstract MyRespuesta verificarEntidadAlEliminar(T entidad);
    
    protected abstract Class getClase();
    //</editor-fold>
}
